package com.coderly.onlinegallerysharingplatform.service.impl;


import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.coderly.onlinegallerysharingplatform.common.convention.errorcode.ErrorCode;
import com.coderly.onlinegallerysharingplatform.common.convention.exception.BizException;
import com.coderly.onlinegallerysharingplatform.enums.UserRoleEnum;
import com.coderly.onlinegallerysharingplatform.model.dao.entity.PictureDO;
import com.coderly.onlinegallerysharingplatform.model.dao.entity.SpaceDO;
import com.coderly.onlinegallerysharingplatform.model.dao.entity.UserDO;
import com.coderly.onlinegallerysharingplatform.model.dao.mapper.PictureMapper;
import com.coderly.onlinegallerysharingplatform.model.dao.mapper.UserMapper;
import com.coderly.onlinegallerysharingplatform.model.dto.req.analyze.*;
import com.coderly.onlinegallerysharingplatform.model.dto.resp.analyze.*;
import com.coderly.onlinegallerysharingplatform.service.AnalyzeService;
import com.coderly.onlinegallerysharingplatform.service.PictureService;
import com.coderly.onlinegallerysharingplatform.service.SpaceService;
import com.coderly.onlinegallerysharingplatform.toolkit.ExceptionTrowUtils;
import com.coderly.onlinegallerysharingplatform.toolkit.UserUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.util.*;
import java.util.stream.Collectors;


@Slf4j
@Service
@RequiredArgsConstructor
public class AnalyzeServiceImpl implements AnalyzeService {

    private final SpaceService spaceService;
    private final PictureService pictureService;
    private final UserMapper userMapper;
    private final PictureMapper pictureMapper;


    @Override
    public SpaceUsageAnalyzeRespDTO getSpaceUsageAnalyze(SpaceUsageAnalyzeReqDTO dto) {
        // 前置权限校验（包含参数合法性检查）
        checkSpaceAnalyzeAuth(dto);

        // 根据查询类型分派处理逻辑
        if (dto.getSelectAll() || dto.getSelectPublic()) {
            return handlePublicSpaceAnalysis(dto);
        } else {
            return handlePrivateSpaceAnalysis(dto.getSpaceId());
        }
    }

    @Override
    public List<SpaceCategoryAnalyzeRespDTO> getSpaceCategoryAnalyze(SpaceCategoryAnalyzeReqDTO spaceCategoryAnalyzeReqDTO) {
        // 检查权限
        checkSpaceAnalyzeAuth(spaceCategoryAnalyzeReqDTO);
        // 构造查询条件
        QueryWrapper<PictureDO> queryWrapper = new QueryWrapper<>();
        // 填充查询范围
        fillSpaceAnalyzeQueryWrapper(spaceCategoryAnalyzeReqDTO, queryWrapper);
        // 使用MP分组查询
        queryWrapper.select("category", "count(*) as count", "sum(pic_size) as totalSize")
                .groupBy("category");
        // 执行查询
        List<Map<String, Object>> categoryMaps = pictureService.getBaseMapper().selectMaps(queryWrapper);
        // 转换结果
        return categoryMaps.stream()
                .map(result -> {
                    // 处理 null 值
                    String category = Optional.ofNullable((String) result.get("category"))
                            .orElse("暂未分类");
                    Long count = ((Number) result.get("count")).longValue();
                    Long totalSize = ((Number) result.get("totalSize")).longValue();
                    return SpaceCategoryAnalyzeRespDTO.builder()
                            .category(category)
                            .count(count)
                            .totalSize(totalSize)
                            .build();
                })
                .collect(Collectors.toList());
    }

    @Override
    public List<SpaceTagAnalyzeRespDTO> getSpaceTagAnalyze(SpaceTagAnalyzeReqDTO spaceTagAnalyzeReqDTO) {
        // 检查权限
        checkSpaceAnalyzeAuth(spaceTagAnalyzeReqDTO);
        // 构造查询条件
        QueryWrapper<PictureDO> queryWrapper = new QueryWrapper<>();
        fillSpaceAnalyzeQueryWrapper(spaceTagAnalyzeReqDTO, queryWrapper);

        // 查询所有符合条件的标签
        queryWrapper.select("tags");
        List<String> tagsJsonList = pictureService.getBaseMapper().selectObjs(queryWrapper)
                .stream()
                .filter(ObjUtil::isNotNull)
                .map(Object::toString)
                .collect(Collectors.toList());

        // 合并所有标签并统计使用次数
        Map<String, Long> tagCountMap = tagsJsonList.stream()
                .flatMap(tagsJson -> JSONUtil.toList(tagsJson, String.class).stream())
                .collect(Collectors.groupingBy(tag -> tag, Collectors.counting()));

        // 转换为响应对象，按使用次数降序排序
        return tagCountMap.entrySet().stream()
                .sorted((e1, e2) -> Long.compare(e2.getValue(), e1.getValue())) // 降序排列
                .map(entry -> SpaceTagAnalyzeRespDTO.builder()
                        .tag(entry.getKey())
                        .count(entry.getValue())
                        .build())
                .collect(Collectors.toList());
    }

    @Override
    public List<SpaceSizeAnalyzeRespDTO> getSpaceSizeAnalyze(SpaceSizeAnalyzeReqDTO spaceSizeAnalyzeReqDTO) {
        // 检查权限
        checkSpaceAnalyzeAuth(spaceSizeAnalyzeReqDTO);

        // 构造查询条件
        QueryWrapper<PictureDO> queryWrapper = new QueryWrapper<>();
        fillSpaceAnalyzeQueryWrapper(spaceSizeAnalyzeReqDTO, queryWrapper);

        // 查询所有符合条件的图片大小
        queryWrapper.select("pic_size");
        List<Long> picSizes = pictureService.getBaseMapper().selectObjs(queryWrapper)
                .stream()
                .map(size -> ((Number) size).longValue())
                .collect(Collectors.toList());

        // 定义分段范围，注意使用有序 Map
        Map<String, Long> sizeRanges = new LinkedHashMap<>();
        sizeRanges.put("<100KB", picSizes.stream().filter(size -> size < 100 * 1024).count());
        sizeRanges.put("100KB-500KB", picSizes.stream().filter(size -> size >= 100 * 1024 && size < 500 * 1024).count());
        sizeRanges.put("500KB-1MB", picSizes.stream().filter(size -> size >= 500 * 1024 && size < 1 * 1024 * 1024).count());
        sizeRanges.put(">1MB", picSizes.stream().filter(size -> size >= 1024 * 1024).count());

        // 转换为响应对象
        return sizeRanges.entrySet().stream()
                .map(entry -> SpaceSizeAnalyzeRespDTO.builder()
                        .sizeRange(entry.getKey())
                        .count(entry.getValue())
                        .build())
                .collect(Collectors.toList());
    }

    @Override
    public List<SpaceUserAnalyzeRespDTO> getSpaceUserAnalyze(SpaceUserAnalyzeReqDTO spaceUserAnalyzeReqDTO) {
        // 检查权限
        checkSpaceAnalyzeAuth(spaceUserAnalyzeReqDTO);

        // 构造查询条件
        QueryWrapper<PictureDO> queryWrapper = new QueryWrapper<>();
        Long userId = spaceUserAnalyzeReqDTO.getUserId();
        queryWrapper.eq(ObjUtil.isNotNull(userId), "user_id", userId);
        fillSpaceAnalyzeQueryWrapper(spaceUserAnalyzeReqDTO, queryWrapper);

        // 分析维度：每日、每周、每月
        String timeDimension = spaceUserAnalyzeReqDTO.getTimeDimension();
        switch (timeDimension) {
            case "day":
                queryWrapper.select("DATE_FORMAT(create_time, '%Y-%m-%d') AS period", "COUNT(*) AS count");
                break;
            case "week":
                queryWrapper.select("YEARWEEK(create_time) AS period", "COUNT(*) AS count");
                break;
            case "month":
                queryWrapper.select("DATE_FORMAT(create_time, '%Y-%m') AS period", "COUNT(*) AS count");
                break;
            default:
                throw new BizException(ErrorCode.PARAMS_ERROR, "不支持的时间维度");
        }

        // 分组和排序
        queryWrapper.groupBy("period").orderByAsc("period");

        // 查询结果并转换
        List<Map<String, Object>> queryResult = pictureService.getBaseMapper().selectMaps(queryWrapper);
        return queryResult.stream()
                .map(result -> {
                    String period = result.get("period").toString();
                    Long count = ((Number) result.get("count")).longValue();
                    return SpaceUserAnalyzeRespDTO.builder()
                            .period(period)
                            .count(count)
                            .build();
                })
                .collect(Collectors.toList());
    }

    @Override
    public List<SpaceDO> getSpaceRankAnalyze(SpaceRankAnalyzeReqDTO spaceRankAnalyzeReqDTO) {
        // 仅管理员可查看空间排行
        ExceptionTrowUtils.throwIf(!UserUtil.isAdmin(), ErrorCode.NO_AUTH_ERROR, "无权查看空间排行");
        // 构造查询条件
        QueryWrapper<SpaceDO> queryWrapper = new QueryWrapper<>();
        queryWrapper.select("id", "space_name", "user_id", "total_size")
                .orderByDesc("total_size")
                .last("LIMIT " + spaceRankAnalyzeReqDTO.getTopN()); // 取前 N 名
        // 查询结果
        return spaceService.list(queryWrapper);
    }


    /**
     * 处理公共库/全空间分析
     */
    private SpaceUsageAnalyzeRespDTO handlePublicSpaceAnalysis(SpaceUsageAnalyzeReqDTO dto) {
        QueryWrapper<PictureDO> queryWrapper = new QueryWrapper<>();
        // 使用SQL聚合函数优化查询性能
        queryWrapper.select("SUM(pic_size) as total_size", "COUNT(*) as total_count");
        fillSpaceAnalyzeQueryWrapper(dto, queryWrapper);

        // 获取聚合结果（单行数据）
        Map<String, Object> resultMap = pictureService.getMap(queryWrapper);

        // 处理空结果集情况
        long usedSize = resultMap != null ? ((Number) resultMap.getOrDefault("total_size", 0L)).longValue() : 0L;
        long usedCount = resultMap != null ? ((Number) resultMap.getOrDefault("total_count", 0L)).longValue() : 0L;

        return SpaceUsageAnalyzeRespDTO.builder()
                .usedSize(usedSize)
                .usedCount(usedCount)
                // 公共空间无限制指标
                .maxSize(null)
                .maxCount(null)
                .sizeUsageRatio(null)
                .countUsageRatio(null)
                .build();
    }

    /**
     * 处理私有空间分析
     */
    private SpaceUsageAnalyzeRespDTO handlePrivateSpaceAnalysis(Long spaceId) {
        SpaceDO space = spaceService.getById(spaceId);

        return SpaceUsageAnalyzeRespDTO.builder()
                .usedSize(space.getTotalSize())
                .maxSize(space.getMaxSize())
                .sizeUsageRatio(calculateUsageRatio(space.getTotalSize(), space.getMaxSize()))
                .usedCount(space.getTotalCount())
                .maxCount(space.getMaxCount())
                .countUsageRatio(calculateUsageRatio(space.getTotalCount(), space.getMaxCount()))
                .build();
    }

    /**
     * 计算使用率（含安全校验）
     */
    private Double calculateUsageRatio(long used, long max) {
        if (max <= 0L) {
            return null; // 或根据业务需求返回0.0/抛出异常
        }
        return NumberUtil.round(used * 100.0 / max, 2).doubleValue();
    }
    /**
     * 校验空间分析权限
     * @param dto 请求参数
     */
    private void checkSpaceAnalyzeAuth(SpaceAnalyzeBaseReqDTO dto) {
        Boolean selectPublic = dto.getSelectPublic();
        Boolean selectAll = dto.getSelectAll();
        Long spaceId = dto.getSpaceId();

        // 参数互斥校验：只能选择一种查询模式
        int optionCount = 0;
        if (Boolean.TRUE.equals(selectAll)) optionCount++;
        if (Boolean.TRUE.equals(selectPublic)) optionCount++;
        if (spaceId != null) optionCount++;
        if (optionCount != 1) {
            throw new BizException(ErrorCode.PARAMS_ERROR, "必须且只能指定一种查询范围（全空间/公共库/指定空间）");
        }

        // 获取用户信息
        String loginUserRole = UserUtil.getLoginUserRole();
        Long loginUserId = UserUtil.getLoginUserId();
        boolean isAdmin = UserRoleEnum.ADMIN.getValue().equals(loginUserRole);

        /* 权限校验逻辑 */
        // 场景1：全空间分析/公共图库分析（仅管理员）
        if (Boolean.TRUE.equals(selectAll) || Boolean.TRUE.equals(selectPublic)) {
            if (!isAdmin) {
                throw new BizException(ErrorCode.NO_AUTH_ERROR, "管理员权限才能进行全空间或公共库分析");
            }
            return;
        }

        // 场景2：指定空间分析（空间所有者或管理员）
        SpaceDO space = spaceService.getById(spaceId);
        if (space == null) {
            throw new BizException(ErrorCode.PARAMS_ERROR, "指定空间不存在");
        }
        if (!isAdmin && !space.getUserId().equals(loginUserId)) {
            throw new BizException(ErrorCode.NO_AUTH_ERROR, "无权限访问该私有空间");
        }
    }


    /**
     * 构建空间分析查询条件
     * @param dto 请求参数
     * @param queryWrapper MyBatis-Plus查询构造器
     */
    private void fillSpaceAnalyzeQueryWrapper(SpaceAnalyzeBaseReqDTO dto, QueryWrapper<PictureDO> queryWrapper) {
        // 根据check方法保证参数合法性，直接按优先级处理

        // 模式1：全空间分析（不限定space_id）
        if (Boolean.TRUE.equals(dto.getSelectAll())) {
            return; // 无附加条件
        }

        // 模式2：公共库分析（space_id为空）
        if (Boolean.TRUE.equals(dto.getSelectPublic())) {
            queryWrapper.isNull("space_id");
            return;
        }

        // 模式3：指定空间分析（限定具体space_id）
        if (dto.getSpaceId() != null) {
            queryWrapper.eq("space_id", dto.getSpaceId());
            return;
        }

        // 理论上不会执行（参数校验已拦截非法情况）
        throw new BizException(ErrorCode.SYSTEM_ERROR, "查询条件构建异常");
    }

    @Override
    public List<TrendAnalyzeRespDTO> getUserRegisterTrend() {
        // 校验管理员权限
        ExceptionTrowUtils.throwIf(!UserUtil.isAdmin(), ErrorCode.NO_AUTH_ERROR, "无权查看用户注册趋势");
        
        // 获取最近7天的日期范围
        LocalDate endDate = LocalDate.now();
        LocalDate startDate = endDate.minusDays(6); // 包括今天在内的7天
        
        // 构建查询
        QueryWrapper<UserDO> queryWrapper = new QueryWrapper<>();
        queryWrapper.select("DATE_FORMAT(create_time, '%Y-%m-%d') AS date", "COUNT(*) AS count")
                .ge("create_time", startDate.toString())
                .le("create_time", endDate.plusDays(1).toString()) // 加1天确保包含今天的所有记录
                .groupBy("date")
                .orderByAsc("date");
        
        // 执行查询
        List<Map<String, Object>> results = userMapper.selectMaps(queryWrapper);
        
        // 创建完整的7天日期序列（包含值为0的日期）
        Map<String, Long> dateCountMap = new LinkedHashMap<>();
        for (int i = 0; i < 7; i++) {
            String date = startDate.plusDays(i).toString();
            dateCountMap.put(date, 0L);
        }
        
        // 填充实际数据
        for (Map<String, Object> result : results) {
            String date = (String) result.get("date");
            Long count = ((Number) result.get("count")).longValue();
            dateCountMap.put(date, count);
        }
        
        // 转换为返回结果
        return dateCountMap.entrySet().stream()
                .map(entry -> TrendAnalyzeRespDTO.builder()
                        .date(entry.getKey())
                        .count(entry.getValue())
                        .build())
                .collect(Collectors.toList());
    }
    
    @Override
    public List<TrendAnalyzeRespDTO> getPictureUploadTrend() {
        // 校验管理员权限
        ExceptionTrowUtils.throwIf(!UserUtil.isAdmin(), ErrorCode.NO_AUTH_ERROR, "无权查看图片上传趋势");
        
        // 获取最近7天的日期范围
        LocalDate endDate = LocalDate.now();
        LocalDate startDate = endDate.minusDays(6); // 包括今天在内的7天
        
        // 构建查询
        QueryWrapper<PictureDO> queryWrapper = new QueryWrapper<>();
        queryWrapper.select("DATE_FORMAT(create_time, '%Y-%m-%d') AS date", "COUNT(*) AS count")
                .ge("create_time", startDate.toString())
                .le("create_time", endDate.plusDays(1).toString()) // 加1天确保包含今天的所有记录
                .groupBy("date")
                .orderByAsc("date");
        
        // 执行查询
        List<Map<String, Object>> results = pictureMapper.selectMaps(queryWrapper);
        
        // 创建完整的7天日期序列（包含值为0的日期）
        Map<String, Long> dateCountMap = new LinkedHashMap<>();
        for (int i = 0; i < 7; i++) {
            String date = startDate.plusDays(i).toString();
            dateCountMap.put(date, 0L);
        }
        
        // 填充实际数据
        for (Map<String, Object> result : results) {
            String date = (String) result.get("date");
            Long count = ((Number) result.get("count")).longValue();
            dateCountMap.put(date, count);
        }
        
        // 转换为返回结果
        return dateCountMap.entrySet().stream()
                .map(entry -> TrendAnalyzeRespDTO.builder()
                        .date(entry.getKey())
                        .count(entry.getValue())
                        .build())
                .collect(Collectors.toList());
    }

}




